package com.tianji.learning.utils;

import ch.qos.logback.core.spi.LifeCycle;
import cn.hutool.json.JSON;
import com.tianji.common.utils.JsonUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@RequiredArgsConstructor
public class LearningRecordDelayTaskHandler {

    private final StringRedisTemplate redisTemplate;
    private final String KEY_TEMPLATE = "learning:record:{}";
    private DelayQueue<DelayTask<DelayTaskData>> delayQueue = new DelayQueue<>();
    private final LearningLessonMapper lessonMapper;
    private final LearningRecordMapper recordMapper;
    private boolean flag = true;

    /**
     * 01 从缓存中读取 学习记录 - 根据 lessonId + sectionId 获取
     *
     * @param lessonId
     * @param sectionId
     * @return
     */
    public LearningRecord readLearningRecordFromCache(Long lessonId, Long sectionId) {
        log.info("01-1从缓存中查询学习记录，课表ID：{}，小节ID：{}", lessonId, sectionId);
        //1 组装大key
        String key = StringUtils.format(KEY_TEMPLATE, lessonId);
        //2 组装hashKey
        String hashKey = sectionId.toString();
        //3 发起查询，获得 hashValue
        String hashValue = (String) redisTemplate.opsForHash().get(key, hashKey);
        if (ObjectUtils.isEmpty(hashValue)) {
            log.info("01-2从缓存中查询学习记录，课表ID：{}，小节ID：{}，未查询到记录", lessonId, sectionId);
            return null;
        }
        //4 转换 hashValue -> learningRecord
        LearningRecord learningRecord = JsonUtils.toBean(hashValue, LearningRecord.class);

        return learningRecord;
    }

    /**
     * 02 将学习记录写入缓存
     *
     * @param learningRecord
     */
    public void writeLearningRecordToCache(LearningRecord learningRecord) {
        log.info("02向缓存中写入学习记录，学习记录数据：{}", learningRecord);
        //1 组装大key
        String key = StringUtils.format(KEY_TEMPLATE, learningRecord.getLessonId());
        //2 组装hashKey
        String hashKey = learningRecord.getSectionId().toString();
        //3 组装hashValue
        String hashValue = JsonUtils.toJsonStr(new RedisRecordData(learningRecord));

        //4 将数据写入redis 设置存活时间60s
        redisTemplate.opsForHash().put(key, hashKey, hashValue);
        //redisTemplate.expire(key, 1, TimeUnit.MINUTES);
        //redisTemplate.expire(key, Duration.ofSeconds(40));
        redisTemplate.expire(key, 60, TimeUnit.SECONDS);

    }

    /**
     * 03 更新缓存，并添加延时任务
     *
     * @param learningRecord
     */
    public void addLearningRecordDelayCheckTask(LearningRecord learningRecord) {
        log.info("03向缓存中更新学习记录，并提交延时检测任务，学习记录数据：{}", learningRecord);
        //1 更新学习记录
        writeLearningRecordToCache(learningRecord);

        //2 添加延时检测任务
        delayQueue.add(new DelayTask<>(Duration.ofSeconds(20), new DelayTaskData(learningRecord)));
    }

    /**
     * 04 清空对应的缓存
     *
     * @param lessonId
     * @param sectionId
     */
    public void clearLearningRecord(Long lessonId, Long sectionId) {
        log.info("04删除缓存中学习记录，课表ID：{}，小节ID：{}", lessonId, sectionId);
        //1 组装大key
        String key = StringUtils.format(KEY_TEMPLATE, lessonId);
        //2 组装hashKey
        String hashKey = sectionId.toString();
        //3 删除redis数据
        redisTemplate.opsForHash().delete(key, hashKey);
    }

    /**
     * 05 开启线程 执行延时任务
     *
     *Bean初始化
     */
    @PostConstruct
    public void init() {
        log.info("Bean初始化阶段执行，开启一个新的线程，执行延时检测任务...");
        new Thread(() -> handleDelayTask()).start();
        //CompletableFuture.runAsync(()->handleDelayTask());
    }

    /**
     * 06 销毁Bean，【优雅地】停止线程
     */
    @PreDestroy
    public void destroy() {
        log.info("Bean销毁阶段执行，关闭线程，执行延时检测任务...");
        //delayQueue.clear();
        flag = false;
    }

    /**
     * 07 执行延时检测任务
     */
    private void handleDelayTask() {
        log.info("07开始执行延时检测任务");
        while (flag) {
            try {
                //1 从delayQueue中take延时检测任务，查看moment
                DelayTask<DelayTaskData> take = delayQueue.take();
                DelayTaskData data = take.getData();
                log.info("从delayQueue中take到延时检测任务数据：{}", data);

                //2 从redis中查询播放进度moment
                LearningRecord redisRecord = readLearningRecordFromCache(data.getLessonId(), data.getSectionId());
                //3 比较2个moment是否一致

                //3.1 【不一致】不进行处理
                if (ObjectUtils.isEmpty(redisRecord) || !ObjectUtils.equal(redisRecord.getMoment(), data.getMoment())) {
                    continue;
                }
                //3.2 【一致】将redis中地数据更新到mysql的学习记录表，并更新课表
                //3.2.1 更新学习记录表（视频进度moment）
                //redisRecord.setFinished(redisRecord.getFinished());
                redisRecord.setFinished(null);
                log.info("更新课程record记录：{}", redisRecord);
                recordMapper.updateById(redisRecord);

                //3.2.2 更新课表（最新学习小节id，最新学习时间）
                LearningLesson lesson =new LearningLesson();
                lesson.setId(data.getLessonId());
                lesson.setLatestSectionId(data.getSectionId());
                lesson.setLatestLearnTime(LocalDateTime.now().minus(Duration.ofSeconds(20)));
                log.info("更新课表lesson数据：{}", lesson);
                lessonMapper.updateById(lesson);

            } catch (InterruptedException e) {
                log.error("延时检测任务执行失败！！！ 原因：{}", e);
            }
        }

    }


    /**
     * tool-01 中转数据包装类 - 写入redis的数据格式
     */
    @Data
    class RedisRecordData {
        private Long id;
        private Integer moment;
        private Boolean finished;

        public RedisRecordData(LearningRecord learningRecord) {
            this.id = learningRecord.getId();
            this.moment = learningRecord.getMoment();
            this.finished = learningRecord.getFinished();
        }
    }

    /**
     * tool-02 中转数据包装类 - 延时加测任务的数据格式
     */
    @Data
    class DelayTaskData {
        private Long lessonId;
        private Long sectionId;
        private Integer moment;

        public DelayTaskData(LearningRecord learningRecord) {
            this.lessonId = learningRecord.getLessonId();
            this.sectionId = learningRecord.getSectionId();
            this.moment = learningRecord.getMoment();
        }
    }

}
